﻿module app.utils;

import std.base64;
import deimos.openssl.aes;
import std.datetime;
import deimos.openssl.aes;
import deimos.openssl.rsa;
import deimos.openssl.sha;
import deimos.openssl.obj_mac;
import deimos.openssl.bio;
import deimos.openssl.pem;
import deimos.openssl.evp;
import std.base64;
import std.digest.sha;
import std.array;
import std.format;
import std.typetuple;
import std.typecons;
import std.algorithm.sorting;

import std.experimental.logger;
string aes256decrypt(string encode, string userKey)
{
	//AES 解密获取data
	const ubyte[] ubSignedData = Base64.decode(encode);
	const ubyte[] ubUserKey = cast(const ubyte[])userKey;
	AES_KEY key;
	AES_set_decrypt_key( ubUserKey.ptr, 256, &key);
	ubyte[] out_ = new ubyte[ubSignedData.length];
	ubyte[32] iv;
	AES_cbc_encrypt(ubSignedData.ptr, out_.ptr,ubSignedData.length , &key,iv.ptr, AES_DECRYPT);
	int paddingLength = out_[$ - 1];
	return cast(string) out_[0 .. $ - paddingLength];
}


string aes256Encrypt(string encode, string userKey)
{
	//AES 解密获取data
	ubyte[] ubSignedData = cast(ubyte[])encode;
	
	long ubTotal = (ubSignedData.length / 16 +1) * 16;
	ubyte[] pkcs7 = new ubyte[16 - ubSignedData.length % 16];
	pkcs7[] = cast(ubyte)(ubTotal - ubSignedData.length);
	
	ubSignedData ~=pkcs7;
	
	const ubyte[] ubUserKey = cast(const ubyte[])userKey;
	AES_KEY key;
	AES_set_encrypt_key( ubUserKey.ptr, 256, &key);
	ubyte[] out_ = new ubyte[ubSignedData.length];
	ubyte[32] iv;
	AES_cbc_encrypt(ubSignedData.ptr, out_.ptr,ubSignedData.length, &key,iv.ptr, AES_ENCRYPT);
	return Base64.encode(out_);
}

/// 返回的是base64
string rsa256SignPrivateKey(string msg, string privateKey)
{
	import std.string;
	privateKey = privateKey.replace("\r", "\n");
	ubyte[] uPrivateKey = cast(ubyte[])privateKey;
	const ubyte[SHA256_DIGEST_LENGTH/*32*/] hash256 = sha256Of(msg);
	BIO* bio = BIO_new_mem_buf(uPrivateKey.ptr, cast(int)uPrivateKey.length);
	assert(bio !is null, "new buff error");
	RSA* rsa = null;
	rsa = PEM_read_bio_RSAPrivateKey(bio, &rsa,null,null);
	assert(rsa !is null, "rsa is error");
	uint siglen = RSA_size(rsa);
	ubyte[] sigret = new ubyte[siglen];
	// 标准PKCS#1 v1.5 RSA RSA_PKCS1_PADDING
	auto status = RSA_sign(NID_sha256, hash256.ptr,cast(uint)SHA256_DIGEST_LENGTH, sigret.ptr, &siglen, rsa);
	assert(status == 1, "sign error");
	RSA_free(rsa);
	BIO_free(bio);
	return Base64.encode(sigret);
}


//验密
bool rsa256VerifyPublicKey(string data, string encryptedMsg, string publicKey)
{
	/*string publicKey = "-----BEGIN PUBLIC KEY-----
	 MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0+1jZB4ed581nQoNkwp0
	 mPQvKYfJbBGNAEbdGIAL1+ZD2OukiUdWiHc8wWXDiPyif+yOCAY6b65aB4que/xA
	 eTbkbu0igI7QTS77UoEiocLtCjnIvoRTu4yBBA/bsiaMs8ksjYKP3UQxcz7xa4Wv
	 uP8919x8vKsq8nndHCqwN8ic293BrtzgV/wJhAdERLaz3p1pj2n7GpsMTvlRugVy
	 GQcW7rytR5w61Lvt+vcOjbf559HerQ7a/XObGBZ0v6zSJCr5Vw8dbcCGWDWZjKmK
	 WZ27A6LEIkoq9dLbDEcG0BPTtsYa84PtC/O6a3xWgkjKgRuicZUNSxHdUUy+K/Qh
	 8wIDAQAB
	 -----END PUBLIC KEY-----";*/
	import std.string;
	const ubyte[SHA256_DIGEST_LENGTH/*32*/] dgst = sha256Of(data);
	publicKey = publicKey.replace("\r", "\n");
	ubyte[] uPublicKey = cast(ubyte[])publicKey;
	BIO* bio = BIO_new_mem_buf(uPublicKey.ptr, cast(int)uPublicKey.length);
	assert(bio !is null, "new buff error");
	RSA* rsa = null;
	rsa = PEM_read_bio_RSA_PUBKEY(bio, &rsa,null,null);
	assert(rsa !is null, "rsa is error");
	uint siglen = RSA_size(rsa);
	ubyte[] sigret = Base64.decode(encryptedMsg);
	// 标准PKCS#1 v1.5 RSA RSA_PKCS1_PADDING
	auto status = RSA_verify(NID_sha256, dgst.ptr,cast(uint)SHA256_DIGEST_LENGTH, sigret.ptr, siglen, rsa);
	RSA_free(rsa);
	BIO_free(bio);
	return status == 1 ? true : false;
}
import Curl = std.net.curl;
import etc.c.curl : CurlOption;

string post(string url, string args) {
	auto res = curlPost(url,args);
	return cast(string) res;
}
string postRaw(string url, string args) {
	auto res = curlPost!()(url,args);
	return cast(string) res;
}
string post(string url, string[string] args) {
	auto res = curlPost(url,args);
	return cast(string) res;
}
ubyte[] curlPost(string ctype = "application/x-www-form-urlencoded", long tmout = 30)(string requestURL,ubyte[] data)
{
	import std.experimental.logger;
	ubyte[] result;
	
	auto http = Curl.HTTP();
	http.handle.set(CurlOption.ssl_verifyhost, 0);
	http.handle.set(CurlOption.ssl_verifypeer, 0);
	http.method = Curl.HTTP.Method.post;
	http.url = requestURL;
	http.setPostData(data, ctype);
	http.operationTimeout = dur!"seconds"(tmout);
	http.onReceive = (ubyte[] data) {result~=data; return data.length; };
	
	http.perform();
	import std.experimental.logger;
	trace("POST RESULT : ",cast(string)result);
	return result;
}

ubyte[] curlPost(string ctype = "application/x-www-form-urlencoded", long tmout = 30)(string requestURL,string[string] postData)
{
	string fpostData = formatPostData(postData);
	ubyte[] upostData = cast(ubyte[])fpostData;
	return curlPost!(ctype,tmout)(requestURL,upostData);
}
ubyte[] curlPost(string ctype = "application/x-www-form-urlencoded", long tmout = 30)(string requestURL,string postData)
{
	ubyte[] upostData = cast(ubyte[])postData;
	return curlPost!(ctype,tmout)(requestURL,upostData);
}
string formatPostData(string[string] postData)
{
	import std.uri;
	string result;
	foreach(key,value;postData)
	{
		result ~= key ~ "=" ~ encodeComponent(value) ~ "&";
	}
	//string endResult = encodeComponent(result);
	return result;
}


Tuple!(K, V)[] aa_sort(K, V)(V[K] aa)
{
	typeof(return) r = [];
	foreach (k, v; aa)
		r ~= tuple(k, v);
	sort!q{a[0]<b[0]}(r);
	return r;
}

///yyyy-MM-dd HH:mm:ss 返回此格式
string getTimeStamp(SysTime time = Clock.currTime())
{
	string tmp = time.toISOExtString();
	import std.array, std.string;
	tmp = tmp.replace("T", " ");
	tmp = tmp[0 .. tmp.indexOf('.')];
	return tmp;
}
string aa_xml(string[string] data)
{
	string[] xml;
	xml ~="<xml>";
	foreach(k,v;data){
		xml ~= format("<%s><![CDATA[%s]]></%s>",k,v,k);
	}
	xml ~="</xml>";
	return xml.join("");
}

string[string] xml_aa_wx(string xml, string rootNodeName = "xml")
{
	import std.string;
	import arsd.dom;
	xml = xml.replace("\n", "");
	XmlDocument docs = new XmlDocument(xml);
	
	Element rootE = docs.querySelector(rootNodeName);
	Element node = rootE.firstChild();
	string[string] hashMap;
	while (node !is null)
	{
		if(node.directText.length)
			hashMap[node.tagName] = node.directText;
		node = node.nextSibling();
	}
	return hashMap;
}


import app.beanstalkd;

private Server _server;

Tube getStalkdTube()
{
	import hunt.text.configuration;
	import hunt, std.conv;
	if(_server is null){
		
		string host = Config.app.config.beanstalkd.host.value();
		ushort port = to!ushort(Config.app.config.beanstalkd.port.value());
		_server = new Server(host,port);
	}
	string tube = Config.app.config.beanstalkd.tube.value();
	trace("tube name:", tube);
	return _server.getTube(tube);
}

string aaKsortSign(string[string] data, string[] except = ["sign"], bool isEncode = false)
{
	import std.stdio, std.uri;
	import std.algorithm.searching;
	
	auto sortbb = aa_sort(data);
	string[] need_sign;
	foreach (cc; sortbb)
	{
		if (cc[1].length == 0 || canFind(except, cc[0]))
		{
			continue;
		}
		need_sign ~= cc[0] ~ "=" ~( isEncode ? encodeComponent(cc[1]) : cc[1]);
	}
	import std.digest.md, std.digest.sha, std.array;
	import std.string : toLower;
	
	trace("to sign string: ", need_sign.join('&') );
	return need_sign.join('&');
}
string aaKsortSign(string[][string] data,string[] except = null, bool needDecode = false)
{
	import std.stdio, std.uri;
	import std.algorithm.searching;
	auto sortbb = aa_sort(data);
	string[] need_sign;
	foreach (cc; sortbb)
	{
		if (cc[1].length == 0 || canFind(except, cc[0]))
		{
			continue;
		}
		need_sign ~= cc[0] ~ "=" ~ (needDecode ? decodeComponent(cc[1][0]) :cc[1][0]);
	}
	import std.digest.md, std.digest.sha, std.array;
	import std.string : toLower;
	
	trace("to sign string: ", need_sign.join('&') );
	return need_sign.join('&');
}